<?php

class model_generator {

	var $fields;
	
	var $dir_name;
	
	function model_generator() {
		
		$tables = $GLOBALS['Database']->get_tables();
		foreach($tables as $table) {
			$fields = $GLOBALS['Database']->get_fields($table);
			foreach ($fields as $fld) {
				$fld_pieces = explode('_', $fld);
				$last_piece = end($fld_pieces);
				$first_piece = array_shift($fld_pieces);
				$details = array();
				switch($last_piece) {
					case 'id':
						if(count($fld_pieces) > 1) {
							$details['name'] = implode('_', $fld_pieces);
							$details['relations'][] = $first_piece .'_'. implode('_', $fld_pieces);
						} else {
							$details['name'] = 'id';
							$details['id'] = true;
						}
						break;
					case 'modified':
						$details['name'] = 'modified';
						$details['modified'] = true;
						break;
					case 'created':
						$details['name'] = 'created';
						$details['created'] = true;
						break;
					case 'order':
						$details['name'] = 'order';
						$details['order'] = true;
						break;
					default:
						$details['name'] = implode('_', $fld_pieces);
						break;
				}
				$this->fields[$table][$fld] = $details;
			}
		}
		$this->generate_classes();
	}
	
	function generate_classes() {
		$dirname = dirname(__FILE__).DIRECTORY_SEPARATOR.'models_'.date('YmdHis');
		if(mkdir($dirname)) {
			$this->dir_name = $dirname;
		}
		foreach($this->fields as $table => $fields) {
			$this->generate_class($table, $fields);
		}
		
		$this->generate_include_file();
		
	}
	
	function generate_include_file() {
		$out .= '<'.'?php'."\n\n";
		foreach($this->fields as $table => $fields) {
			$table_name_pieces = explode('_', $table);
			array_shift($table_name_pieces);
			$class_name = implode('_', $table_name_pieces);
			$out .= 'include_once "'.$class_name.'_base.class.php";'."\n";
			$out .= 'include_once "'.$class_name.'.class.php";'."\n";
		}
		$out .= "\n\n".'?'.'>';
		$full_filename = $this->dir_name.DIRECTORY_SEPARATOR.'include_all.php';
		$fh = fopen($full_filename, 'w');
		if (fwrite($fh, $out, strlen($out))) {
			fclose($fh);
			print $full_filename .' includefile generated.<br />';
		}
	}
	
	function generate_class($table, $fields) {
		$table_name_pieces = explode('_', $table);
		array_shift($table_name_pieces);
		$class_name = implode('_', $table_name_pieces);
		
		// testo classe base
		$class_txt  = "<"."?php\n\n";
		$class_txt .= "class ". $class_name ."_base {\n\n";
		$class_txt .= $this->generate_vars();
		$class_txt .= $this->generate_constructor($class_name, $fields);
		$class_txt .= $this->generate_add($table, $fields);
		$class_txt .= $this->generate_mod($table, $fields);
		$class_txt .= $this->generate_del($table, $fields);
		$class_txt .= $this->generate_get_all($table, $fields);
		$class_txt .= $this->generate_get_by_field($table, $fields);
		$class_txt .= $this->generate_vars_methods($table, $fields);
		$class_txt .= $this->generate_ordering_methods($table, $fields);
		$class_txt .= "}\n\n";
		$class_txt .= "?".">";
		
		// genero la classe base
		$full_filename = $this->dir_name.DIRECTORY_SEPARATOR.$class_name.'_base.class.php';
		$fh = fopen($full_filename, 'w');
		if (fwrite($fh, $class_txt, strlen($class_txt))) {
			fclose($fh);
			print $full_filename .' base generated.<br />';
		}
		
		// testo classe "final"
		$class_txt = "<"."?php\n\n";
		$class_txt .= "class ". $class_name ." extends ".$class_name."_base {\n\n";
		$class_txt .= "\tfunction $class_name() {\n";
		$class_txt .= "\t\t".'$this->'.$class_name."_base();\n";
		$class_txt .= "\t}\n\n";
		$class_txt .= "}\n\n";
		$class_txt .= "?".">";
		
		// genero la classe "final"
		$full_filename = $this->dir_name.DIRECTORY_SEPARATOR.$class_name.'.class.php';
		$fh = fopen($full_filename, 'w');
		if (fwrite($fh, $class_txt, strlen($class_txt))) {
			fclose($fh);
			print $full_filename .' generated.<br />';
		}
		
	}
	
	function generate_constructor($class_name, $fields) { // TODO parametri sorting paging
		
		$first_fld_name = $id_fld_name = $order_fld_name = false;
		
		foreach($fields as $fld_name => $data) {
			if (!$first_fld_name) {
				$first_fld_name = $fld_name;
			}
			if ($data['id']) {
				$id_fld_name = $fld_name;
			} else if ($data['order']) {
				$order_fld_name = $fld_name;
			}
		}
		$default_order = ($order_fld_name ? $order_fld_name : ($id_fld_name ? $id_fld_name : $first_fld_name));
		
		$out  = "\tfunction ". $class_name ."_base() {\n";
		$out .= "\t\t".'$this->set_order_by("'. $default_order .' ASC");'."\n";
		$out .= "\t\t".'$this->set_paginate();'."\n";
		$out .= "\t\t".'$this->set_rows_per_page();'."\n";
		$out .= "\t\t".'$this->set_page();'."\n";
		$out .= "\t\t".'$this->set_custom_where();'."\n";
		$out .= "\t}\n\n";
		return $out;
	}
	
	function generate_add($table, $fields) {
		$modified_field = false;
		$created_field = false;
		$order_field = false;
		$field_list = array();
		foreach($fields as $fld_name => $data) {
			if ($data['modified']) {
				$modified_field = $fld_name;
			} else if ($data['created']) {
				$created_field = $fld_name;
			} else if ($data['order']) {
				$order_field = $fld_name;
			} else if (!$data['id'] && !$data['order']) {
				$field_list[$fld_name] = $data['name'];
			}
		}
		$out  = "\tfunction add($". implode(', $', $field_list) .") {\n";
		$out .= "\t\t".'$qp = array();'."\n";
		$out .= "\t\t".'$q  = "INSERT INTO '. $table .' SET ";'."\n";
		foreach($field_list as $fn => $f) {
			$out .= "\t\t".'$qp[] = "'. $fn ." = '\".". 'mysql_real_escape_string($'. $f .')."\'";'."\n";
		}
		if ($modified_field) {
			$out .= "\t\t".'$qp[] = "'. $modified_field .' = NOW()";'."\n";
		}
		if ($created_field) {
			$out .= "\t\t".'$qp[] = "'. $created_field .' = NOW()";'."\n";
		}
		if ($order_field) {
			$out .= "\t\t".'$qp[] = "'. $order_field .' = ". $this->_get_max_order();'."\n";
		}
		$out .= "\t\t".'$q .= implode(\',\', $qp);'."\n";
		$out .= "\t\t".'if ($GLOBALS[\'Database\']->query($q)) {'."\n";
		$out .= "\t\t\t".'return $GLOBALS[\'Database\']->last_id();'."\n";
		$out .= "\t\t}\n";
		$out .= "\t\treturn false;\n";
		$out .= "\t}\n\n";
		return $out;
	}
	
	function generate_mod($table, $fields) {
		$modified_field = false;
		$field_list = array();
		foreach($fields as $fld_name => $data) {
			if ($data['modified']) {
				$modified_field = $fld_name;
			} else if (!$data['created'] && !$data['order']) {
				$field_list[$fld_name] = $data['name'];
				if ($data['id']) {
					$id_fld_name = $fld_name;
				}
			}
		}
		$out  = "\tfunction mod($". implode(', $', $field_list) .") {\n";
		$out .= "\t\t".'$qp = array();'."\n";
		$out .= "\t\t".'$q  = "UPDATE '. $table .' SET ";'."\n";
		foreach($field_list as $fn => $f) {
			if ($f != 'id') {
				$out .= "\t\t".'$qp[] = "'. $fn ." = '\".". 'mysql_real_escape_string($'. $f .')."\'";'."\n";
			}
		}
		if ($modified_field) {
			$out .= "\t\t".'$qp[] = "'. $modified_field .' = NOW()";'."\n";
		}
		$out .= "\t\t".'$q .= implode(\',\', $qp);'."\n";
		$out .= "\t\t".'$q .= "WHERE '. $id_fld_name .' = ".$id;'."\n";
		$out .= "\t\t".'if ($GLOBALS[\'Database\']->query($q)) {'."\n";
		$out .= "\t\t\t".'return $id;'."\n";
		$out .= "\t\t}\n";
		$out .= "\t\treturn false;\n";
		$out .= "\t}\n\n";
		return $out;	
	}
	
	function generate_del($table, $fields) {
		foreach($fields as $fld_name => $data) {
			if ($data['id']) {
				$id_fld_name = $fld_name;
			}
		}
		$out  = "\tfunction del(".'$id) {'."\n";
		$out .= "\t\t". '$q = "DELETE FROM '. $table .' WHERE '. $id_fld_name .' = ". $id;'."\n";
		$out .= "\t\t". 'return $GLOBALS[\'Database\']->query($q);'."\n";
		$out .= "\t}\n\n";
		return $out;
	}
	
	function generate_get_all($table, $fields) {
		$out  = "\tfunction get_all_count() {\n";
		$out .= "\t\t".'$q = "SELECT COUNT(*) AS c FROM '.$table.'";'."\n";
		$out .= "\t\t".'if ($this->custom_where) $q .= " WHERE ". $this->custom_where ." ";'."\n";
		$out .= "\t\t".'$r = $GLOBALS[\'Database\']->query2obj($q);'."\n";
		$out .= "\t\t".'return $r->c;'."\n";
		$out .= "\t}\n\n";
		
		$out .= "\tfunction get_all() {\n";
		$out .= "\t\t".'$q  = "SELECT * FROM '.$table.'";'."\n";
		$out .= $this->find_joins($fields);
		$out .= "\t\t".'if ($this->custom_where) $q .= " WHERE ". $this->custom_where ." ";'."\n";
		$out .= "\t\t".'$q .= " ORDER BY ". $this->order_by;'."\n";
		$out .= "\t\t".'if ($this->paginate) {'."\n";
		$out .= "\t\t\t".'$q .= " LIMIT ". ($this->page*$this->rows_per_page) .", ". ($this->rows_per_page);'."\n";
		$out .= "\t\t}\n";
		$out .= "\t\t".'return $GLOBALS[\'Database\']->query2oa($q);'."\n";
		$out .= "\t}\n\n";
		return $out;
	}
	
	function generate_get_by_field($table, $fields) {
		foreach($fields as $fld_name => $data) {
			$out .= "\tfunction get_by_". $data['name'] ."($". $data['name'] .") {\n";
			$out .= "\t\t".'$q  = "SELECT * FROM '.$table.'";'."\n";
			$out .= $this->find_joins($fields);
			$out .= "\t\t".'$q .= " WHERE '. $fld_name ." = '\".".'$'.$data['name'] .".\"'\";\n";
			$out .= "\t\t".'if ($this->custom_where) $q .= " AND ". $this->custom_where ." ";'."\n";
			if ($data['id']) {
				$out .= "\t\t".'return $GLOBALS[\'Database\']->query2obj($q);'."\n";
			} else {
				$out .= "\t\t".'$q .= " ORDER BY ". $this->order_by;'."\n";
				$out .= "\t\t".'if ($this->paginate) {'."\n";
				$out .= "\t\t\t".'$q .= " LIMIT ". ($this->page*$this->rows_per_page) .", ". ($this->rows_per_page);'."\n";
				$out .= "\t\t}\n";
				$out .= "\t\t".'return $GLOBALS[\'Database\']->query2oa($q);'."\n";
			}
			$out .= "\t}\n\n";
		}
		return $out;
	}
	
	function generate_vars() {
		$out  = "\tvar ".'$use_joins;'."\n";
		$out .= "\tvar ".'$order_by;'."\n";
		$out .= "\tvar ".'$paginate;'."\n";
		$out .= "\tvar ".'$page;'."\n";
		$out .= "\tvar ".'$rows_per_page;'."\n";
		$out .= "\tvar ".'$custom_where;'."\n\n";
		return $out;
	}
	
	function generate_vars_methods() {
		$out  = "\t". 'function set_custom_where($where = null) {'."\n";
		$out .= "\t\t".'$this->custom_where = $where;'."\n";
		$out .= "\t}\n\n";
		
		$out .= "\t". 'function set_use_joins($use_joins = false) {' ."\n";
		$out .= "\t\t".'$this->use_joins = (bool) $use_joins;'."\n";
		$out .= "\t}\n\n";
		
		$out .= "\t". 'function set_order_by($order_by) {' ."\n";
		$out .= "\t\t".'$this->order_by = $order_by;'."\n";
		$out .= "\t}\n\n";
		
		$out .= "\t". 'function set_paginate($paginate = false) {' ."\n";
		$out .= "\t\t".'$this->paginate = (bool) $paginate;'."\n";
		$out .= "\t}\n\n";
		
		$out .= "\t". 'function set_rows_per_page($rows_per_page = 25) {' ."\n";
		$out .= "\t\t".'$this->rows_per_page = $rows_per_page;'."\n";
		$out .= "\t}\n\n";
		
		$out .= "\t".'function set_page($page = 0) {' ."\n";
		$out .= "\t\t" .'$this->page = $page;'."\n";
		$out .= "\t}\n\n";
		return $out;
	}
	
	function generate_ordering_methods($table, $fields) {
		$order_field = false;
		$id_field = false;
		foreach($fields as $fld_name => $data) {
			if ($data['order']) {
				$order_field = $fld_name;
			} else if ($data['id']) {
				$id_field = $fld_name;
			}
		}
		if ($order_field) {
			// _get_max_order
			$out  = "\t".'function _get_max_order() {'."\n";
			$out .= "\t\t".'$q = "SELECT MAX('. $order_field .') AS max FROM '. $table .'";'."\n";
			$out .= "\t\t".'if ($this->custom_where) $q .= " WHERE ". $this->custom_where ." ";'."\n";
			$out .= "\t\t".'$r = $GLOBALS[\'Database\']->query2obj($q);'."\n";
			$out .= "\t\t".'if($r->max > 0) return ($r->max + 1);'."\n";
			$out .= "\t\t".'return 1;'."\n";
			$out .= "\t}\n\n";
			
			// move
			$out .= "\t".'function move($id, $dir = \'up\') {'."\n";
			
			$out .= "\t\t".'if ($dir == \'up\') {'."\n";
			$out .= "\t\t\t".'$ascdesc = \'asc\';'."\n";
			$out .= "\t\t\t".'$ltgt = \'>\';'."\n";
			$out .= "\t\t".'} else {'."\n";
			$out .= "\t\t\t".'$ascdesc = \'desc\';'."\n";
			$out .= "\t\t\t".'$ltgt = \'<\';'."\n";
			$out .= "\t\t}\n\n";
			
			$out .= "\t\t".'$q1 = "SELECT '. $id_field .', '. $order_field .' FROM '. $table .' WHERE '. $id_field .' = ". $id;'."\n";
			$out .= "\t\t".'if ($this->custom_where) $q1 .= " AND ". $this->custom_where ." ";'."\n";
			$out .= "\t\t".'$r1 = $GLOBALS[\'Database\']->query2obj($q1);'."\n\n";
			
			$out .= "\t\t".'$q2  = "SELECT '. $id_field .', '. $order_field .' FROM '. $table .' WHERE '. $order_field .' ". $ltgt ." ". $r1->'. $order_field;
			$out .= "\t\t".'if ($this->custom_where) $q2 .= " WHERE ". $this->custom_where ." ";'."\n";
			$out .= "\t\t".'$q2 .= " ORDER BY '. $order_field .' ". $ascdesc;'."\n";
			$out .= "\t\t".'$r2 = $GLOBALS[\'Database\']->query2obj($q2);'."\n\n";
			
			$out .= "\t\t".'$q3 = "UPDATE '. $table .' SET '. $order_field .' = ". $r1->'. $order_field .' ." WHERE '. $id_field .' = ". $r2->'. $id_field .';'."\n";
			$out .= "\t\t".'if ($this->custom_where) $q3 .= " WHERE ". $this->custom_where ." ";'."\n";
			$out .= "\t\t".'$r3 = $GLOBALS[\'Database\']->query($q3);'."\n\n";
			
			$out .= "\t\t".'$q4 = "UPDATE '. $table .' SET '. $order_field .' = ". $r2->'. $order_field .' ." WHERE '. $id_field .' = ". $r1->'. $id_field .';'."\n";
			$out .= "\t\t".'if ($this->custom_where) $q4 .= " WHERE ". $this->custom_where ." ";'."\n";
			$out .= "\t\t".'$r4 = $GLOBALS[\'Database\']->query($q4);'."\n\n";
			
			$out .= "\t\t".'return ($r3 && $r4);'."\n";
			
			$out .= "\t".'}'."\n\n";
			return $out;
		}
	}
	
	function find_joins($fields) {
		foreach($fields as $fld_name => $data) {
			if (is_array($data['relations'])) {
				foreach($data['relations'] as $rel) {
					foreach($this->fields as $tname => $tfields) {
						$tname_pieces = explode('_', $tname);
						array_shift($tname_pieces);
						$match_tname = implode('_', $tname_pieces);
						
						$rel_pieces = explode('_', $rel);
						array_pop($rel_pieces);
						array_shift($rel_pieces);
						$match_fname = implode('_', $rel_pieces);
						
						if ($match_fname == $match_tname) {
							foreach($tfields as $matched_fields => $data) {
								if ($data['id']) {
									$out .= "\t\t".'if ($this->use_joins) $q .= " JOIN '.$tname.' ON '. $matched_fields .' = '. $rel .'";'."\n";
									continue 2; // TODO CHECK ???
								}
							}
						}
					}
				}
			}
		}
		return $out;
	}
	
}

?>